package cloudapi

import (
	"context"
	"fmt"
	"github.com/cloudwego/hertz-examples/bizdemo/hertz_gorm/biz/config"
	"github.com/cloudwego/hertz-examples/bizdemo/hertz_gorm/biz/hertz_gen/user_manager"
	"github.com/cloudwego/hertz/pkg/common/hlog"
	"github.com/tencentyun/cos-go-sdk-v5"
	"github.com/tencentyun/qcloud-cos-sts-sdk/go"
	"net/http"
	"net/url"
	"strings"
	"time"
)

const (
	DispositionKey = "response-content-disposition"
	Inline         = "inline"
	Attachment     = "attachment"
)

type CosProxy struct {
	client      *sts.Client
	cosClient   *cos.Client
	innerClient *cos.Client
}

func NewCosProxy() *CosProxy {
	bucketURL, _ := url.Parse(config.GetGlobalConfig().Cloud.BaseUrl)
	//https://{private_bucket}.cos.{region}.myqcloud.com
	writeBucketURL, _ := url.Parse(fmt.Sprintf("https://%v.cos.%v.myqcloud.com",
		config.GetGlobalConfig().Cloud.PrivateBucket, config.GetGlobalConfig().Cloud.Region))
	return &CosProxy{
		client: sts.NewClient(
			config.GetGlobalConfig().Cloud.SecretId,
			config.GetGlobalConfig().Cloud.SecretKey,
			nil,
		),
		cosClient: cos.NewClient(
			&cos.BaseURL{
				BucketURL: bucketURL,
			},
			&http.Client{
				Transport: &cos.AuthorizationTransport{
					SecretID:  config.GetGlobalConfig().Cloud.SecretId,
					SecretKey: config.GetGlobalConfig().Cloud.SecretKey,
					Transport: &http.Transport{DisableKeepAlives: false},
				},
			},
		),
		innerClient: cos.NewClient(
			&cos.BaseURL{
				BucketURL: writeBucketURL,
			},
			&http.Client{
				Transport: &cos.AuthorizationTransport{
					SecretID:  config.GetGlobalConfig().Cloud.SecretId,
					SecretKey: config.GetGlobalConfig().Cloud.SecretKey,
					Transport: &http.Transport{DisableKeepAlives: false},
				},
			},
		),
	}
}

var credentialOptTemplate = &sts.CredentialOptions{
	DurationSeconds: int64(time.Hour.Seconds()),
	Region:          config.GetGlobalConfig().Cloud.Region,
	Policy: &sts.CredentialPolicy{
		Statement: []sts.CredentialPolicyStatement{
			{
				// 密钥的权限列表。简单上传和分片需要以下的权限，其他权限列表请看 https://cloud.tencent.com/document/product/436/31923
				Action: []string{
					// 简单上传
					"name/cos:PostObject",
					"name/cos:PutObject",
					// 分片上传
					"name/cos:InitiateMultipartUpload",
					"name/cos:ListMultipartUploads",
					"name/cos:ListParts",
					"name/cos:UploadPart",
					"name/cos:CompleteMultipartUpload",
				},
				Effect: "allow",
				Resource: []string{
					// 这里改成允许的路径前缀，可以根据自己网站的用户登录态判断允许上传的具体路径，例子： a.jpg 或者 a/* 或者 * (使用通配符*存在重大安全风险, 请谨慎评估使用)
					// 存储桶的命名格式为 BucketName-APPID，此处填写的 bucket 必须为此格式
					"*",
					"qcs::cos:" + config.GetGlobalConfig().Cloud.Region + ":uid/" +
						config.GetGlobalConfig().Cloud.AppId + ":" + config.GetGlobalConfig().Cloud.PrivateBucket + "*",
				},
				// 开始构建生效条件 condition
				// 关于 condition 的详细设置规则和COS支持的condition类型可以参考https://cloud.tencent.com/document/product/436/71306
				Condition: map[string]map[string]interface{}{},
			},
		},
	},
}

type ReqOption func(*sts.CredentialOptions)

func WithRegion(region string) ReqOption {
	return func(o *sts.CredentialOptions) {
		o.Region = region
	}
}

func (p *CosProxy) GetCredential(opt ...ReqOption) (*sts.CredentialResult, error) {
	for _, option := range opt {
		option(credentialOptTemplate)
	}
	return p.client.GetCredential(credentialOptTemplate)
}

func (p *CosProxy) GetPreSignedURL(ctx context.Context, accessKey string, optionType user_manager.FileOption) (string, error) {
	// 获取预签名
	conf := config.GetGlobalConfig().Cloud
	preSignedURL, err := p.cosClient.Object.GetPresignedURL(ctx, http.MethodGet, accessKey, conf.SecretId,
		conf.SecretKey, time.Hour, nil)
	if err != nil {
		return "", err
	}
	url := preSignedURL.String()
	if optionType == user_manager.FileOption_View {
		url = fmt.Sprintf("%v&ci-process=doc-preview&dstType=png", url)
	}
	fileName := accessKey[strings.LastIndex(accessKey, "/")+1:]
	url = fmt.Sprintf("%v&response-content-disposition=%s", url, fmt.Sprintf("attachment:filename=%s", fileName))
	return url, nil
}

func (p *CosProxy) UpLoadFile(ctx context.Context, key, filePath string) (string, error) {
	// Case1 多线程上传对象
	opt := &cos.MultiUploadOptions{
		ThreadPoolSize: 5,
	}
	v, _, err := p.innerClient.Object.Upload(
		ctx, key, filePath, opt,
	)
	if err != nil {
		return "", err
	}
	hlog.CtxInfof(ctx, "Case1 done, %v\n", v.Key)
	return v.Key, nil
}

func (p *CosProxy) DownloadFile(ctx context.Context, key, filePath string) error {
	opt := &cos.MultiDownloadOptions{
		ThreadPoolSize: 5,
	}
	_, err := p.innerClient.Object.Download(
		ctx, key, filePath, opt,
	)
	return err
}
